package com.tjcd.project.library_base.base.rsaUtils;


import com.tjcd.project.library_base.base.rsaUtils.binary.Base64;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

/**
 * @Author jiangKunKun
 * @CreateDate 2021/7/6
 * @Description RSA加密解密工具类
 * @SpittingGrooves
 */
public class RSAUtil {
    public static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDArPDJm7Ey+VGOHdj5X6AH8KGlhmZ7/MTVY3cGY87wJTcS+kAun/Cn1Kk9JnCR946c7b5kReWb9un0I/zFrRgFC369n35I+DbV7lcECs05DseSQo4QmsEzNZrDpQLhFmyHO8JbpYypP1T9XrRGMs3eBCHVBi9ZB//oNG6p9gxpjwIDAQAB";

    public static final String privateKeyFirst = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMCs8MmbsTL5UY4d2PlfoAfwoaWGZnv8xNVjdwZjzvAlNxL6QC6f8KfUqT0mcJH3jpztvmRF5Zv26fQj/MWtGAULfr2ffkj4NtXuVwQKzTkOx5JCjhCawTM1msOlAuEWbIc7wluljKk/VP1etEYyzd4EIdUGL1kH/+g0bqn2DGmPAgMBAAECgYAmIxUWEsckO800wBuuYIsghzS3ur6WVBXx1to8XY0cY4FfNEF3+Rh9X86P2Af4HHQSA8zntB4anOj59hWWWwihvo7U6Vn2JsigXzXNM3BcR866yz2PCk7vS8FXKCWdnlGC5y+a2Spe64lzbKhXzy+O30KvNE2rPx7+nM6si7u04QJBAPe1tbDpOUp4DuOlmn3anGOdnQf+7DTT7G4T1+1AcGgtutyq+p3/CC3c5Y/Niek8YVLGTAkK4NODV0LXzn4FqXMCQQDHH7d9P2wF3ZSaUc8Kvv3CtROU29N88CpjGo8DhqX3SAKWxd+iA2ekSe8c5duitAWYxtgG6efPPgMYAd9KFCh1AkA2NVfLnV5iQ5/OhcEnZZ3v1uA0jb8XWIFXHZSVYRK4WbmY7PnndZZOoxbBv5M8rXAG80pUeEL3Rrjj8eJIheitAkEAjKRRRIyULrxnpmvb8boen/SnpP9zLA6x0ZfDpXjoJnVdJsMSBmujO33g/qc3b375aQQhNxjk2xLwuncwEDGWtQJAIXDO38OeaxWg3qhr5/E5yxIx7O4Sj5a36c83INTS7Jjxcz/u7YOVgsYKmYEE5cbOmbGLBqge4usH+Qht72cMnA==";

    public static final String publicKeySecond = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCS6bOnPe+YZM3jScNOsrK1QNOy2nvmX1aMQudmoHE9FqhX/q3XMn8jejtZ4eriVQOCOxaQX6sG3PLv68IDsdxH8gnx0qI/Yy4X59YnZRYltsVgfeLAfCN1q050hTpRa9nyz5JaGlpGdLkiL5KJRdsF1g/UiryzIC66zxb4lqm2GQIDAQAB";

    public static final String PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJLps6c975hkzeNJw06ysrVA07Lae+ZfVoxC52agcT0WqFf+rdcyfyN6O1nh6uJVA4I7FpBfqwbc8u/rwgOx3EfyCfHSoj9jLhfn1idlFiW2xWB94sB8I3WrTnSFOlFr2fLPkloaWkZ0uSIvkolF2wXWD9SKvLMgLrrPFviWqbYZAgMBAAECgYAXg+THrjg9klUKPnJOlHfaX0gDZslSNIxr4wGbAa9ecdP8Ub2Npn2HNjdtyaWJGMZ4//A6p7V+YbtY/8mMQdnaQo4PcaG/07/sHa/EoiF5uoKg598pciCN2oBDn6Aagm8kuEIXFOs1xYAIgXlbTR7aZgFbB2GGyKORVBCU2aMxAQJBAPMEWFOZUUuEh0PV59IXZ11czT/AM7GXZ96FfjDmtLwXIh2Oj9hbuqoW3jDTUll+cCDMUr/tcLWAQ3zdF+jZXNECQQCawvnmesOwrspwdk7ai6gkS9dd9NVJ/FzLg1vBhIPH+no4tMkPz+rN3Cb3VNeWv3t6HSrMNiS8uEcChjEPn/bJAkATveqe//mGtQF4FUx72MLg5sUVxLtuVepuPVH/Dv7nuMlgtVAiN3F2l9z9Am3SL19UisS+Kr/ZPs48uolStTXBAkBF0XxoDC5IgTJ29giw85xRc52fl5q5nA5JCePVM/E3wLpu5rBmX8hwQseZgT8ZlR0hhBMQucRtFz2B2AQ+Jk5xAkEAzF8kM+pbZGbTfL4xtLXgU63Os5kyuFGFzAS2cb3Vy8Kt5qgI1+lzHLh541VOxaAAzvgkxIeccvlI6Kf76AhUXg==";

    public static final String CHARSET = "utf-8";
    public static final String KEY_ALGORITHM = "RSA";
    public static final String SIGN_ALGORITHM = "SHA1WithRSA";
    public static final String CIPHER_TRANSFORMATION = "RSA";

    /**
     * RSA最大加密明文大小 ：
     * The RSA algorithm can only encrypt data that has a maximum byte length of the RSA key length in bits divided with eight minus eleven padding bytes, i.e. number of maximum bytes = key length in bits / 8 - 11.
     * 1024/8-11=128-11=117
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * RSA秘钥/公钥对 生成
     *
     * @return void
     */
    public static void keyPairGenerator() {
        try {
            KeyPairGenerator keygen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
            keygen.initialize(1024);

            //生成密钥公钥 pubkey 和私钥 prikey
            KeyPair keys = keygen.generateKeyPair(); // 生成密钥组
            PublicKey pubkey = keys.getPublic();
            PrivateKey prikey = keys.getPrivate();
            byte[] pk = pubkey.getEncoded();
            byte[] privk = prikey.getEncoded();
            String strpk = new String(Base64.encodeBase64(pk));  //原始公钥做Base64编码
            String strprivk = new String(Base64.encodeBase64(privk));    //原始私钥做Base64编码

            System.out.println("公钥Base64编码:" + strpk);
            System.out.println("私钥Base64编码:" + strprivk);
        } catch (Exception e) {
//			LOG.error(e);
        }
    }

    /**
     * RSA签名
     *
     * @param content    待签名数据
     * @param privateKey 商户私钥
     * @return String 签名后的结果，并做了Base64编码
     */
    public static String sign(String content, String privateKey) {
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));    //对于 Private key 是用 PKCS#8 编码
            KeyFactory keyf = KeyFactory.getInstance(KEY_ALGORITHM);
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);

            Signature signature = Signature.getInstance(SIGN_ALGORITHM);
            signature.initSign(priKey);
            signature.update(content.getBytes(CHARSET));
            byte[] signed = signature.sign();
            return Base64.encodeBase64String(signed);
        } catch (Exception e) {
//			LOG.error(e);
        }
        return null;
    }

    /**
     * RSA验证签名
     *
     * @param content   待签名数据
     * @param sign      已数据签名结果，并做了Base64编码
     * @param publicKey 支付宝公钥
     * @return boolean
     */
    public static boolean verifySign(String content, String sign, String publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            byte[] encodedKey = Base64.decodeBase64(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            Signature signature = Signature.getInstance(SIGN_ALGORITHM);
            signature.initVerify(pubKey);
            signature.update(content.getBytes(CHARSET));
            boolean bverify = signature.verify(Base64.decodeBase64(sign));
            return bverify;
        } catch (Exception e) {
//			LOG.error(e);
        }
        return false;
    }

    /**
     * RSA加密
     *
     * @param content   需要加密的信息
     * @param publicKey 公钥
     * @return String 加密后的结果，并做了Base64编码
     */
    public static String encrypt(String content, String publicKey) {
        try {
            X509EncodedKeySpec pubX509 = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            PublicKey pubKey = keyFactory.generatePublic(pubX509);
            //
            Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            byte[] data = content.getBytes(CHARSET);
            int inputLen = data.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] cache;
            // 对数据分段加密
            for (int offSet = 0; offSet < inputLen; offSet += MAX_ENCRYPT_BLOCK) {
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(data, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
            }
            byte[] encryptBytes = out.toByteArray();
            out.close();
            return Base64.encodeBase64String(encryptBytes);
        } catch (Exception e) {
//			LOG.error(e);
        }
        return null;
    }

    /**
     * RSA解密
     *
     * @param content    加密后的结果，并做了Base64编码
     * @param privateKey 私钥
     * @return String 解密结果
     */
    public static String decrypt(String content, String privateKey) {
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));    //对于 Private key 是用 PKCS#8 编码
            KeyFactory keyf = KeyFactory.getInstance(KEY_ALGORITHM);
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);
            //
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            byte[] data = Base64.decodeBase64(content);
            int inputLen = data.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] cache;
            // 对数据分段解密
            for (int offSet = 0; offSet < inputLen; offSet += MAX_DECRYPT_BLOCK) {
                byte[] subData = subArray(data, offSet, MAX_DECRYPT_BLOCK);
                cache = cipher.doFinal(subData);
                out.write(cache, 0, cache.length);
            }
            byte[] decryptBytes = out.toByteArray();
            out.close();
            return new String(decryptBytes, CHARSET);
        } catch (Exception e) {
//			LOG.error(e);
            return null;
        }
    }

    /**
     * 将base64编码后的公钥字符串转成PublicKey实例
     *
     * @param publicKey 公钥字符
     * @return publicKEY
     * @throws Exception exception
     */
    public static PublicKey getPublicKey(String publicKey) throws Exception {
        byte[] keyBytes = android.util.Base64.decode(publicKey, android.util.Base64.DEFAULT);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(keySpec);
    }


    /**
     * RSA加密
     *
     * @param content   待加密文本
     * @param publicKey 公钥
     * @return 密文
     * @throws Exception exception
     */
    public static String encrypt(String content, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");//java默认"RSA"="RSA/ECB/PKCS1Padding"
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] data = content.getBytes();
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return new String(android.util.Base64.encode(encryptedData, android.util.Base64.DEFAULT));
    }


    /**
     * 截取byte数组中指定子集
     *
     * @param source 原byte数组
     * @param offset 起始索引
     * @param limit  截取的byte数组的最大长度
     * @return byte[]
     */
    private static byte[] subArray(byte[] source, int offset, int limit) {
        int targetLen = 0;
        if (source == null || offset >= source.length || offset < 0 || limit <= 0) {
            return new byte[]{};
        } else if (source.length - offset > limit) {
            targetLen = limit;
        } else {
            targetLen = source.length - offset;
        }
        byte[] target = new byte[targetLen];
        for (int i = 0; i < targetLen; i++) {
            target[i] = source[offset + i];
        }
        return target;
    }

    /**
     * 是用证书，签名字符串
     *
     * @param text
     * @param privateKeyPath
     * @param charset
     * @return
     * @throws Exception
     */
    public static String signForFile(String text, String privateKeyPath, String charset) throws Exception {
        FileInputStream fis = new FileInputStream(privateKeyPath);
        ObjectInputStream ois = new ObjectInputStream(fis);
        PrivateKey privateK = (PrivateKey) ois.readObject();
        return sign(text, privateK, charset);
    }

    public static String sign(String text, PrivateKey privateK, String charset) throws Exception {
        Signature signature = Signature.getInstance(SIGN_ALGORITHM);
        signature.initSign(privateK);
        signature.update(getContentBytes(text, charset));
        byte[] result = signature.sign();
        return Base64.encodeBase64String(result);
    }

    /**
     * @param content
     * @param charset
     * @return
     * @throws UnsupportedEncodingException
     */
    private static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }

    /**
     * 描述该方法的功能及算法流程
     *
     * @param args
     * @return void
     * @throws UnsupportedEncodingException
     */

    public static void main(String[] args) {
        String decrypt = RSAUtil.decrypt("LZZzK8X3VQmK5trXFPHtbBWkPfk3oLnhViT9ASjmDUeLEkoawLEbNjLiPxykB5cLW//m4Xo19SRdtOs91w9PpEEj3GxEFB3R0n0yoJGBcrg0Fm0xQHvtuiazxDSe7NwZIPOCRbLsatdXh3RV/UPb+H756ONbPk9ILaqwBetrfLEAGvum/DcxnU6bR5n/SGLC7CsmqBMWCnO/STTOFihYTIqO5i/zfnMO0fjTQMdIK15GYKlUuTg8OhD88ZfqJWShaIVdd582SbnNj2682v57ceeshYhHb27poMoJHef1+mHXW+rAmZz7KzhTQBWoTu33ax0a7UBPRo26FnYg0lgGrDKp79wwcCXkcG+5HJoeuAcB/kYBN7YHPS7FndhQI9gKGiN8TZDP9aYXIIpOwacV0cpKctKkazfi43lJc+R30vYWy5Q+N7uGmvQNO5pcUasRlHgtU1c1BzrI81Qn7wj84ZZEg0vhEknTqsbFBJLFkehDhaC39LSyvLjqdSnOvQOu",
                PRIVATE_KEY);
        System.out.println(decrypt);
    }
}
